/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: timer.cpp
 *
 * Purpose: timer implementation.
 *
 * Developer:
 *   wen.gu , 2021-04-02
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "icpp/executor/timer.h"

#include <condition_variable>
#include <mutex>
#include <chrono>

#include "icpp/executor/thread.h"
#include "icpp/core/utils.h"

#define LOG_TAG  "Timer"
#include "icpp/core/log.h"

namespace icpp
{
namespace executor
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

class Timer::Impl
{
public:
    bool cancel_wait_ = false;
    uint32_t start_interval_ms_ = 0;
    uint32_t periodic_interval_ms_ = 0;
    uint32_t skipped_ = 0;
    Thread* thd_ = nullptr;
    int64_t  next_trigger_ = 0;
    TimerHandler handler_ = nullptr;    
    std::mutex lock_;
    std::condition_variable cond_var_;
public:
    void onTimer()
    {
        int64_t interval = 0;

        do
        {
            int64_t wait_time = 0; /** unit in milliseconds*/
            do
            {
                wait_time = core::NowSteadyMs() - next_trigger_;

                if (wait_time < 0)
                {
                    if (interval == 0)
                    {
                        wait_time = 0;
                        break;
                    }

                    next_trigger_ += interval;
                    skipped_++;
                }
                
            } while (wait_time < 0);
            
            core::AutoLock al(lock_);
            if (!cancel_wait_ && (cond_var_.wait_for(al, std::chrono::milliseconds(wait_time)) == std::cv_status::timeout))
            {               
                if (handler_)
                {
                    al.unlock();
                    handler_();
                    al.lock();
                }                
            }
            else
            {
                next_trigger_ = core::NowSteadyMs();
            }

            interval = periodic_interval_ms_;
            next_trigger_ += interval;
            skipped_ = 0;
        } while (interval > 0);
        
    }

    bool initTimer(TimerHandler handler)
    {
        if (handler_)
        {
            LOGE("Timer already running\n");
            return false;
        }

        if (handler)
        {
            handler_ = handler;
            cancel_wait_ = false;
            thd_ = new Thread("timer", [this]() -> bool
            {
                this->onTimer();
                return false;
            });

            return true;
        }   

        return false; 
    }

    bool startTimer()
    {
        if (handler_)
        {
            next_trigger_ = core::NowSteadyMs() + start_interval_ms_; /** todo, use steady time or system time?? */
            return (thd_ != nullptr) ? thd_->start() : false;            
        }

        return false;
    }

    bool setTimerPriority(int32_t priority)
    {
        if (handler_)
        {
            return (thd_ != nullptr) ? thd_->set_priority(priority): false;
        }

        return false;
    }

    bool restart(uint32_t periodic_interval_ms)
    {
        if (handler_)
        {
            periodic_interval_ms_ = periodic_interval_ms;
            cond_var_.notify_all();
            return true;            
        }
        return false;
    }

    bool stop()
    {
        
        if (handler_ && thd_)
        {
            {
                core::AutoLock al(lock_);
                periodic_interval_ms_ = 0;
                cancel_wait_ = true;
                cond_var_.notify_all();
            }

            if (thd_->isSelf())
            {
                LOGE("cann't call Timer::stop in TimerHandler callback function, this will be deadlock\n");
            }

            thd_->join();

            delete thd_;
            thd_ = nullptr;
            handler_ = nullptr;
            return true;
        }

        return false;
    }
};

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
/*
    * Create a timer
    *
    * @param [in] start_interval_ms: how many time delay before timer first start.
    * @param [in] periodic_interval_ms: the timeout interval of timer; > 0: timer is cyclic, = 0: only run once. 
    * @return None
    */
Timer::Timer(uint32_t start_interval_ms /*= 0*/, uint32_t periodic_interval_ms /*= 0*/)
    :impl_(new Impl)
{
    impl_->start_interval_ms_ = start_interval_ms;
    impl_->periodic_interval_ms_ = periodic_interval_ms;

    /** todo something */ 
}

Timer::~Timer()
{
    /** todo something */
    impl_->stop();
}


bool Timer::start(TimerHandler handler)
{
    if (impl_->initTimer(handler))
    {
        return impl_->startTimer();
    }

    return false;
}

bool Timer::start(TimerHandler handler, int32_t priority)
{
    if (impl_->initTimer(handler) && impl_->setTimerPriority(priority))
    {
        return impl_->startTimer();
    }

    return false;
}

bool Timer::stop()
{
    return impl_->stop();
}

/**
 * Restarts the periodic interval. If the callback method is already running, nothing will happen.
 */
bool Timer::restart()
{
    return restart(impl_->periodic_interval_ms_);
}

/**
 * Sets a new periodic interval and restarts the timer. An interval of 0 will stop the timer.
 */
bool Timer::restart(uint32_t periodic_interval_ms)
{
    return impl_->restart(periodic_interval_ms);
}


uint32_t Timer::start_interval() const
{
    return impl_->start_interval_ms_;
}

uint32_t Timer::periodic_interval() const
{
    return impl_->periodic_interval_ms_;
}

/**
 * Sets the start interval. Will only be effective before start() is called.
 */
void Timer::set_start_interval(uint32_t start_interval_ms)
{
    impl_->start_interval_ms_ = start_interval_ms;
}

/**
 * Sets the periodic interval. If the timer is already running the new interval will be 
 * effective when the current interval expires.
 */
void Timer::set_periodic_interval(uint32_t periodic_interval_ms)
{
    impl_->periodic_interval_ms_ = periodic_interval_ms;
}

/**
 * Returns the number of skipped invocations since the last invocation.
 * Skipped invocations happen if the timer callback(TimerHandler) function takes
 * longer to execute than the timer interval(periodic interval).
 */
uint32_t Timer::skipped() const
{
    return impl_->skipped_;
}

} /** namespace executor */
} /** namespace icpp */


